package com.ry.framework.shiro.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import com.ry.framework.shiro.filter.AuthenticationFilter;
import com.ry.framework.shiro.filter.KickoutSessionControlFilter;
import com.ry.framework.shiro.filter.MyLogoutFilter;
import com.ry.framework.shiro.realm.MyRealm;
import com.ry.framework.shiro.session.MySessionManager;
import com.ry.framework.shiro.session.RedisSessionDao;

import net.sf.ehcache.CacheManager;

@Configuration
public class ShiroConfiguration {

	private SimpleCookie rememberMeCookie() {
		// 这里的Cookie的默认名称是 CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME
		SimpleCookie cookie = new SimpleCookie(CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME);
		// 是否只在https情况下传输
//		cookie.setSecure(false);
		cookie.setHttpOnly(true);
		// 设置 cookie 的过期时间，单位为秒，这里为一天
		cookie.setMaxAge(1800000);
		return cookie;
	}

	private CookieRememberMeManager rememberMeManager() {
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		// rememberMe cookie 加密的密钥
		cookieRememberMeManager.setCipherKey(Base64.decode("ZWvohmPdUsAWT3=KpPqda"));
		return cookieRememberMeManager;
	}
	
	 /**
     * FormAuthenticationFilter 过滤器 过滤记住我
     * @return
     */
	@Bean
    public FormAuthenticationFilter formAuthenticationFilter(){
        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        //对应前端的checkbox的name = rememberMe
        formAuthenticationFilter.setRememberMeParam("rememberMe");
        return formAuthenticationFilter;
    }

	@Bean
	public EhCacheManager ehCacheManager() {
		// 使用ehcache缓存，授权不去查数据库
//		EhCacheManager cacheManager = new EhCacheManager();
//		cacheManager.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
//		return cacheManager;
		
		//使用ehcache缓存 授权数据，只是首次需要查询数据库，之后都走缓存，如果是分布式，每台机器都是首次查询，之后走本机ehcache缓存
		CacheManager cacheManager = CacheManager.getCacheManager("es");
        if(cacheManager == null){
            try {
                cacheManager = CacheManager.create(ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml"));
            } catch (IOException e) {
                throw new RuntimeException("initialize cacheManager failed");
            }
        }
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(cacheManager);
        return ehCacheManager;
		
	}

	 /**
     * SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件
     * MemorySessionDAO 直接在内存中进行会话维护
     * EnterpriseCacheSessionDAO  提供了缓存功能的会话维护，默认情况下使用MapCache实现，内部使用ConcurrentHashMap保存缓存的会话。
     * @return
     */
    @Bean
    public SessionDAO sessionDAO() {
        RedisSessionDao redisSessionDAO = new RedisSessionDao();
//        redisSessionDAO.setRedisManager(redisManager());
        //session在redis中的保存时间,最好大于session会话超时时间
//        redisSessionDAO.setExpire(12000);
        return redisSessionDAO;
    }
	
	// 将自己的验证方式加入容器
	@Bean
	public MyRealm myShiroRealm() {
		MyRealm myrealm = new MyRealm();
		return myrealm;
	}

	// 自定义session管理
	@Bean
	public SessionManager sessionManager() {
		DefaultWebSessionManager sessionManager= new MySessionManager();
		sessionManager.setSessionDAO(sessionDAO());
		sessionManager.setSessionIdUrlRewritingEnabled(false);	//去掉login页面的 JSESSIONID
		return sessionManager;
	}

	// 权限管理，配置主要的Realm的管理认证
	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myShiroRealm());
		securityManager.setRememberMeManager(rememberMeManager());
		securityManager.setCacheManager(ehCacheManager());
//		securityManager.setCacheManager(cacheManager());
		// 注入自定义sessionManager
		securityManager.setSessionManager(sessionManager());

		return securityManager;
	}

	// 拦截 根路径
	// 不定义访问根路径会报错，UnavailableSecurityManagerException
	@Bean
	public FilterRegistrationBean<DelegatingFilterProxy> delegatingFilterProxy(){
	    FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean = new FilterRegistrationBean<DelegatingFilterProxy>();
	    DelegatingFilterProxy proxy = new DelegatingFilterProxy();
	    proxy.setTargetFilterLifecycle(true);
	    proxy.setTargetBeanName("shiroFilter");
	    filterRegistrationBean.setFilter(proxy);
	    return filterRegistrationBean;
	}
	
	// Filter 工厂，设置对应的过滤条件和跳转条件
	@Bean("shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
	
		// 自定义过滤器
		Map<String, Filter> filterMap = new LinkedHashMap<>();
		filterMap.put("corsAuthenticationFilter", corsAuthenticationFilter());
		filterMap.put("kickout", kickoutSessionControlFilter());
//		filterMap.put("logout", logoutFilter());
		shiroFilterFactoryBean.setFilters(filterMap);
		
		Map<String, String> map = new LinkedHashMap<String, String>();
		map.put("/", "user");
		// 登出
		//前后端分离的项目，尽量使用controller中的退出，不适用shiro自带的，存在跨域
		//所以自定义的logout过滤器也无法使用，是需要配合这个使用的
//		map.put("/logout", "anon");
		map.put("/test", "anon");
		map.put("/static/**", "anon");
		map.put("/login", "kickout,anon");

		map.put("/**", "kickout,corsAuthenticationFilter");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

		return shiroFilterFactoryBean;
	}

	// 自定义 未登录请求 过滤器
	public AuthenticationFilter corsAuthenticationFilter() {
		return new AuthenticationFilter();
	}
	
	// 自定义 logout 过滤器
	public LogoutFilter logoutFilter() {
		MyLogoutFilter logoutFilter = new MyLogoutFilter();
        return logoutFilter;
	}

	/**
	 * 并发登录控制
	 * @return
	 */
	@Bean
	public KickoutSessionControlFilter kickoutSessionControlFilter(){
	    KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
	    //用于根据会话ID，获取会话进行踢出操作的；
	    kickoutSessionControlFilter.setSessionManager(sessionManager());
	    //使用cacheManager获取相应的cache来缓存用户登录的会话；用于保存用户—会话之间的关系的；
	    kickoutSessionControlFilter.setCacheManager(ehCacheManager());
	    //是否踢出后来登录的，默认是false；即后者登录的用户踢出前者登录的用户；
	    kickoutSessionControlFilter.setKickoutAfter(false);
	    //同一个用户最大的会话数，默认1；比如2的意思是同一个用户允许最多同时两个人登录；
	    kickoutSessionControlFilter.setMaxSession(1);
	    //被踢出后重定向到的地址；前后端不分离的项目，可以直接跳到login页；前后端分离的直接在filter中返回信息，这里不用配置了
	    kickoutSessionControlFilter.setKickoutUrl("/login?kickout=1");
	    return kickoutSessionControlFilter;
	}
	
	@Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
	
	// 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
	// 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	// 开启aop注解支持，也可在pom中引入 对应aop jar包，两种方式都可以
	@Bean
	@ConditionalOnMissingBean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
		defaultAAP.setProxyTargetClass(true);
		return defaultAAP;
	}
}
